今天要來做更進階的自訂控制項,不只能夠修改內建的一些樣式,夠能夠讓我們使用自己想要的圖示來做控制項,甚至變更每個控制項的功能。
今日就來做把左上控制項改成刪除功能。
物件控制一些基礎樣式調整可以參考
Day 15 - Fabricjs 物件控制項樣式調整
因為 fabricjs 並沒有提供 api 能夠讓我們去改變物件的控制項,所以我們必須透過複寫原來 fabricjs 所繪畫控制項的方法,來達到自訂控制項的目的。
這邊講一下自己再複寫的心得,到 fabricjs docs 找有沒有對應的方法。可以看到我找到了 Object.drawControls
這個方法。
接著看到的 Source
點選 line 15906
連結可觀看 fabricjs 原始碼這個方法的所在地。
點選 line 15906
就能看到 drawControls
原本的樣貌啦
fabric.Object.prototype.drawControls
我們想要做的是改變所有產生出來的物件的控制項,所以我們必須要來複寫 fabricjs 原本畫出控制項的方法。
也就是 fabric.Object.prototype.drawControls
,這個方法為 fabricjs 原本畫出控制項圖示的方法。
再覆蓋之前,我們得先去參考原本的寫法如何,再來做客製化修改。
Fabricjs 原始碼 #line15906 drawControls
// 變更所有物件畫出的控制項
fabric.Object.prototype.drawControls = function (ctx, styleOverride) {
// 複寫他,改成什麼都不畫
}
const rect = new fabric.Rect({
width: 100,
height: 100,
left: 100,
top: 100
})
canvas.add(rect)
可看到因為把 drawControls
覆蓋過去了,所以看不到任何控制項。
接著直接將
fabricjs drawControls method source: http://fabricjs.com/docs/fabric.js.html#line15906
整段 call 下來複寫,就會正常畫出控制項了。
接著分析一下原始碼,可以發現控制向都是透過一個名為 _drawControl
的私有函數所畫出來的。
// top-left
this._drawControl('tl', ctx, methodName,
left,
top, styleOverride);
把任何一個拿掉就會少掉那一個控制項。
接著我們把原本的 _drawControl
拿掉,換成我們自己的方式畫出控制項。
// top-left
const cancel = new Image()
cancel.src = 'https://upload.wikimedia.org/wikipedia/commons/6/65/Crystal_button_cancel.svg'
ctx.drawImage(cancel, left, top, this.cornerSize, this.cornerSize)
OK,這樣一來,就能夠有自己定義的控制項圖示了。
不過這時我們是想將左上角的控制向改成刪除的功能,但發現只是改變了圖示,並沒有連功能都一起被改變。
fabric.Object
的 prototype接下來來改寫原本是縮放功能的控制項,改成刪除的功能。
一樣先看了 fabricjs doc 不過發現好像沒有什麼線索,只好透過 google 大神。
大神告訴我 _getActionFromCorner
這個方法,看起來就像是我們所需要的,稍微在看了一下 fabricjs 原始碼,大概了解了前因後果。
__onMouseDown
當在 canvas
中點下後觸發。其中就判斷一些你是否點在物件上,若點選在物件上則觸發 _setupCurrentTransform
來設定接下來要轉變資訊。_setupCurrentTransform
設定轉變資訊,其中有一個 _getActionFromCorner
方法判斷點選的控制項使用什麼方法。_getActionFromCorner
紀錄會做的動作,因為原本的控制項就只有縮放和旋轉,都只是在之後 mousemove
發生時會做的動作,所以只有紀錄接下來會發生甚麼事情,而我們要改變的刪除方法,加在這邊就可以了。fabric.Canvas.prototype._getActionFromCorner
這邊很簡單就複寫 _getActionFromCorner
方法,在 switch case 加上自己自訂的方法就可以了。
fabric.Canvas.prototype._getActionFromCorner = function(target, corner, e) {
if (!corner) {
return 'drag';
}
switch (corner) {
case 'mtr':
return 'rotate';
case 'ml':
break;
case 'mr':
return e[this.altActionKey] ? 'skewY' : 'scaleX';
case 'mt':
case 'mb':
return e[this.altActionKey] ? 'skewX' : 'scaleY';
case 'tl': // 增加左上刪除動作
canvas.remove(target)
return 'deleted'
default:
return 'scale';
}
}
最後我們將原本縮放的箭頭樣式,改成手指 pointer
樣式。
直接修改 fabric.Canvas.prototype.cursorMap
這個陣列就可以了。
var cursorOffset = {
mt: 0, // n
tr: 1, // ne
mr: 2, // e
br: 3, // se
mb: 4, // s
bl: 5, // sw
ml: 6, // w
tl: 7 // nw
}
fabric.Canvas.prototype.cursorMap[7] = 'pointer'
這樣就完成客製化自己的控制項囉,將左上角改為刪除功能
fabricjs 對使用自訂控制項,並沒有提供簡易的 api 來變換控制項。
但還是能透過修改原本 fabricjs 提供的方法來達到需求。
缺點是若版本不同,作法可能會不同,因為不是正規提供的方法,且要複寫必須要深入 fabricjs 原始碼了解原本寫法,較麻煩。
但有人提供了一個良好的解決方案 - fabricjs-customise-controls-extension
來解決這個問題,明天再來介紹。
Ability to customize controls $150
有人懸賞了 150 美元為了解決這個問題,其實一些 fabricjs 主要的貢獻者也有想要修改此問題,加入能夠讓開發者好用的 api,從 2015 年就開始討論此問題 (客製化 controls icon & 功能),不過到現在還沒有被完美的被解決。
貢獻者表示他還有其他更重要的事情要做 XD
最新的進展是寫上面那個 extension 的人想協助將套件加入 fabricjs 核心,不過因為一直在更新似乎還沒有看到結果。
寫這篇文章時版本為
2.4.3
有个疑问 当我在某个控件上使用了setControlsVisibility设置这个控件某个Control的可见性后,其它的控件也被修改了,有什么好的解决办法嘛?